# -*- coding: utf-8 -*-
"""
    Created by huangyi at 2020/9/1.
    Copyright (c) 2013-present, Xiamen Dianchu Technology Co.,Ltd.
    Description:
    Changelog: all notable changes to this file will be documented
"""
import os
from functools import wraps

from app.aop.aop_container import NormalAopContainer, AopContainer
from app.aop.bean_proxy import BeanProxy
from app.aop.processor import Processor
from app.bean.bean_definition import BeanScope
from app.bean.bean_factory import BeanFactory
from app.bean.factory.normal_bean_factory import NormalBeanFactory
from app.errors.unsupport import UnSupportException


class ApplicationContext(BeanFactory, AopContainer):
    """
    应用上下文
    """

    def __init__(self, bean_factory: BeanFactory = None):
        self.aop_container = NormalAopContainer()
        self.bean_factory = bean_factory if bean_factory else NormalBeanFactory()

    def add_processor(self, processor_instance):
        self.aop_container.add_processor(processor_instance)

    def get_processors(self, bean_name):
        return self.aop_container.get_processors(bean_name)

    def get_bean(self, *args, **kwargs):
        bean = self.bean_factory.get_bean(*args, **kwargs)
        return self.generate_bean_aop_proxy(bean)

    def generate_bean_aop_proxy(self, bean):
        """
        生成aop代理对象
        :param bean:
        :return:
        """
        bean_proxy = BeanProxy(bean_instance=bean, fn_find_processor=self.get_processors)
        return bean_proxy

    def get_bean_by_name(self, name):
        bean = self.bean_factory.get_bean_by_name(name)
        return self.generate_bean_aop_proxy(bean)

    def get_bean_by_type(self, type0):
        return self.bean_factory.get_bean_by_type(type0)

    def add_bean_by_interface(self, interface, bean_class, scope):
        self.bean_factory.add_bean_by_interface(interface=interface, bean_class=bean_class, scope=scope)

    def add_bean_by_type(self, bean_class, scope):
        self.bean_factory.add_bean_by_type(bean_class=bean_class,
                                           scope=scope)

    def add_bean_by_name(self, name, bean_class, scope):
        self.bean_factory.add_bean_by_name(name=name,
                                           bean_class=bean_class,
                                           scope=scope)

    def reg_aop_processor(self, pattern=""):
        """
        注册aop处理器
        :return:
        """
        def wrap_reg_aop_processor(cls):
            instance = cls(pattern)
            if isinstance(instance, Processor):
                self.add_processor(processor_instance=instance)
            return cls
        return wrap_reg_aop_processor

    def get_component(self, *args, **kwargs):
        """
        获取组件
        :param args:
        :param kwargs:
        :return:
        """

        def wrap_component(func):
            @wraps(func)
            def fn(*fn_args, **fn_kwargs):
                bean_proxy = self.get_bean(*args, **kwargs)
                return bean_proxy

            return fn

        return wrap_component

    def reg_component(self, *args, **kwargs):
        """
        注册组件类装饰器
        :param args:
        按名称注册：args[0]:名称
        按接口注册: args[0]: 接口类
        按类注册：无参
        :param kwargs:
        scope: BeanScope
        :return:
        """

        def wrap_component(cls):
            scope = kwargs.get("scope", BeanScope.Singleton)
            if not args:
                # 无参数，注册自身
                self.add_bean_by_type(cls, scope=scope)
            elif isinstance(args[0], str):
                # 按名称注册
                self.add_bean_by_name(name=args[0], bean_class=cls, scope=scope)
            elif isinstance(args[0], type(object)):
                # 按接口注册
                self.add_bean_by_interface(interface=args[0], bean_class=cls, scope=scope)
            else:
                raise UnSupportException(f"不支持的参数方式：{args}")
            return cls

        return wrap_component

    def scan(self, root_path, scan_path):
        """
        扫描目录，加载python文件
        :return:
        """
        root_path = os.path.abspath(os.path.dirname(root_path))
        scan_path = scan_path.replace("/", os.sep)
        path = os.path.join(root_path, scan_path)
        for root, sub_dirs, files in os.walk(path):
            for file in files:
                if file.endswith(".py"):
                    file_path = os.path.join(root, file)
                    if not os.path.exists(file_path):
                        continue
                    path = file_path[len(os.getcwd()) + 1:]
                    import_path = path.replace(os.sep, ".")[:-3]
                    __import__(import_path)
